package ldh.im.fxbase.component;

import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
import ldh.im.fxbase.cache.SoftId;
import ldh.im.fxbase.util.MapUtil;

import java.lang.ref.SoftReference;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class BigDataListView<T extends SoftId> extends ListView<T> {

    private WeakHashMap<String, Node> weakHashMap = new WeakHashMap<>();
    private Map<String, SoftReference<Node>> softReferenceMap = new HashMap<>();
    private Map<String, AtomicInteger> softCountMap = new HashMap<>();
    private Callback<T, Node> cellCallback;

    private int softTotal = 10;
    private int softCountTotal = softTotal * 3;

    public BigDataListView() {
        this(10);
    }

    public BigDataListView(ObservableList<T> datas) {
        this(10, datas);
    }

    public BigDataListView(int softTotal) {
        super();
        if (softTotal < 1) {
            throw new IllegalArgumentException("softTotal必须大于0");
        }
        this.softTotal = softTotal;
//        this.setCache(true);
        initCellFactory();
    }

    public BigDataListView(int softTotal, ObservableList<T> data) {
        super(data);
        if (softTotal < 1) {
            throw new IllegalArgumentException("softTotal必须大于0");
        }
        this.softTotal = softTotal;
//        this.setCache(true);
        initCellFactory();
    }

    public void setCellCallback(Callback<T, Node> cellCallback) {
        this.cellCallback = cellCallback;
    }

    public void setSoftTotal(int count) {
        if (softTotal < 1) {
            throw new IllegalArgumentException("softTotal必须大于0");
        }
        this.softTotal = count;
    }

    private Callback<T, Node> getCellCallback() {
        return cellCallback;
    }

    private void initCellFactory() {
        this.setCellFactory(new SoftListCell(this));
//        this.getSelectionModel().selectedItemProperty().addListener((ob, o, n)->{
//            T selectItem = this.getSelectionModel().getSelectedItem();
//            if (selectItem == null) return;
//            String id = selectItem.getSoftId();
//            incrViewCount(id);
//        });
    }

    private Node getSoftMapValue(String id) {
        SoftReference<Node> softReference = (SoftReference<Node>) softReferenceMap.get(id);
        if (softReference != null) {
            Node softNode = softReference.get();
            if (softNode != null) {
                return softNode;
            }
        }
        return null;
    }

    private void weakToSoft() {
        if (softCountMap.size() < 1) return;
        Map<String, AtomicInteger> sortMap = MapUtil.sortMapByValue(softCountMap, (a1, a2)->a2.get()-a1.get());
        int idx = 0;
        softReferenceMap.clear();
        for(Map.Entry<String, AtomicInteger> entry : sortMap.entrySet()) {
            if (idx > softTotal) break;
            Node cellNode = getCell(entry.getKey());
            if (cellNode != null) {
                idx++;
                SoftReference<Node> node = new SoftReference<>(cellNode);
                softReferenceMap.putIfAbsent(entry.getKey(), node);
            }
        }
    }

    private Node getCell(String id) {
//        Node softNode = getSoftMapValue(id);
//        if (softNode != null) {
//            return softNode;
//        }

        Node weakNode = weakHashMap.get(id);
        if (weakNode != null) {
            return weakNode;
        }
        return null;
    }

    private void incrViewCount(String id) {
        AtomicInteger countInt = softCountMap.get(id);
        if (countInt == null) {
            if (softCountMap.size() > softCountTotal) {
                List<String> removes = new ArrayList();
                int count = softCountTotal/5;
                for(Map.Entry<String, AtomicInteger> entry : softCountMap.entrySet()) {
                    if (removes.size() > count) break;
                    if(entry.getValue().get() < 2) {
                        removes.add(entry.getKey());
                    }
                }
                if (removes.size() > 0) {
                    removes.forEach(key->{
                        softCountMap.remove(key);
                        softReferenceMap.remove(key);
                    });
                    countInt = new AtomicInteger(0);
                    softCountMap.put(id, countInt);
                }
            } else {
                countInt = new AtomicInteger(0);
                softCountMap.put(id, countInt);
            }

        }
        if (countInt != null) {
            countInt.incrementAndGet();
        }

        weakToSoft();
    }


    private class SoftListCell<T extends SoftId> implements Callback<ListView<T>, ListCell<T>> {

        private BigDataListView<T> bigDataListView;

        public SoftListCell(BigDataListView<T> bigDataListView) {
            this.bigDataListView = bigDataListView;
        }

        @Override
        public ListCell<T> call(ListView<T> tListView) {
            return new ListCell<T>() {

                @Override
                protected void updateItem(T item, boolean empty) {
                    super.updateItem(item, empty);
                    if (bigDataListView.getCellCallback() == null) throw new RuntimeException("请设置CellCallBack!");
                    if (item == null) return;
                    String id = item.getSoftId();
                    Node cellNode = bigDataListView.getCell(id);
                    if (cellNode != null) {
//                        if(softReferenceMap.size() < softTotal) {
//                            bigDataListView.incrViewCount(id);
//                        }
//                        AtomicInteger ai = softCountMap.get(id);
//                        String v = ai == null ? "null" : ai.get()+"";
//                        System.out.println("cache id:" + id + ", count:" + v + ", size: " + softReferenceMap.size());
                        setCellData(cellNode, item, empty);
                        return;
                    }

                    Node node = newCellData(item, empty);
                    if (node == null) return;
                    weakHashMap.put(id, node);
                }

                private void setCellData(Node node, T item, boolean empty) {
                    if (item == null || empty) {
                        setGraphic(null);
                        setText(null);
                    } else {
                        if (node == null) return;
                        setGraphic(node);
                        setText(null);
                    }
                }

                private Node newCellData(T item, boolean empty) {
                    if (item == null || empty) {
                        setGraphic(null);
                        setText(null);
                        return null;
                    } else {
                        Node node = bigDataListView.getCellCallback().call(item);
                        if (node == null) return null;
                        setGraphic(node);
                        setText(null);
                        return node;
                    }
                }
            };

        }
    }
}
